
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "err_code_mk.h"
#include "iotv30_config.h"
#include "platform.h"
#include "sys_msg_process.h"
#include "ota_manager.h"
#include "print_log.h"

#include "pb_common.h"
#include "pb_encode.h"
#include "pb_decode.h"
#include "iot_message.pb.h"

#include "proto_plat.h"
#include "uart_printf.h"

#include "pb_kt_ota_device.h"



// static const char log_tag[]="[pc_cmd]";
// static int ota_busy_flag;
// static udp_socket_cb_t ota_socket_param;


static ota_param_t pb_ota_param;



/************************************************************************/

const char *ota_step_str[]={
	"ota idle",
	"OTA_STAGE_GW_DOWNLOAD",
	"OTA_STAGE_GW_CHK",
	"OTA_STAGE_GW_SAVE",
	"OTA_STAGE_TRANSMIT_NODE",
	"OTA_STAGE_NODE_CHK",
	"OTA_STAGE_DEVICE_BURN",
	"OTA_STAGE_FINISH",
	"null()"
};

#if 0

int unpack_pkg_CMD_SET_DEVICE_OTAFW_PKG_START(const void *pdata, int len, const udp_socket_cb_t *param)
{    
   log_debug("%s:%s", log_tag, __FUNCTION__);
   const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   const unsigned char *data = (unsigned char *)pdata;
   const frame_head_t *frame_head = &(tlv_frame->frame_head);
   data += sizeof(kt_tlv_frame_t);
    return 0;
}
int unpack_pkg_CMD_SEND_DEVICE_OTAFW_PKG(const void *pdata, int len, const udp_socket_cb_t *param)
{    
   log_debug("%s:%s", log_tag, __FUNCTION__);
   const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   const unsigned char *data = (unsigned char *)pdata;
   const frame_head_t *frame_head = &(tlv_frame->frame_head);
   data += sizeof(kt_tlv_frame_t);
    return 0;
}
int unpack_pkg_CMD_SET_DEVICE_OTAFW_PKG_END(const void *pdata, int len, const udp_socket_cb_t *param)
{    
   log_debug("%s:%s", log_tag, __FUNCTION__);
   const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   const unsigned char *data = (unsigned char *)pdata;
   const frame_head_t *frame_head = &(tlv_frame->frame_head);
   data += sizeof(kt_tlv_frame_t);
    return 0;
}
int unpack_pkg_CMD_QUERY_DEVICE_FW_CHK(const void *pdata, int len, const udp_socket_cb_t *param)
{    
   log_debug("%s:%s", log_tag, __FUNCTION__);
   const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   const unsigned char *data = (unsigned char *)pdata;
   const frame_head_t *frame_head = &(tlv_frame->frame_head);
   data += sizeof(kt_tlv_frame_t);
    return 0;
}
int unpack_pkg_CMD_SET_DEVICE_AUTO_UPGRADE(const void *pdata, int len, const udp_socket_cb_t *param)
{    
   log_debug("%s:%s", log_tag, __FUNCTION__);
   const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   const unsigned char *data = (unsigned char *)pdata;
   const frame_head_t *frame_head = &(tlv_frame->frame_head);
   data += sizeof(kt_tlv_frame_t);
    return 0;
}


int pb_cmd_ota_read_flash_size(const void *pdata, int inlen, const udp_socket_cb_t *param)
{	 
   	log_debug("%s:%s", log_tag, __FUNCTION__);
   	const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   	const unsigned char *data = (unsigned char *)pdata;
   	const frame_head_t *frame_head = &(tlv_frame->frame_head);
   	data += sizeof(kt_tlv_frame_t);

	GetOtaDiskSpaceUp rsp_pkg;
	get_ota_disk_space_up__init(&rsp_pkg);
	rsp_pkg.ack = 1;
	rsp_pkg.space = 102410240;  //  这个数值是随便写的。实际写入设备时真正读一次。
	rsp_pkg.support = 2;

	int ret = -1;
	size_t len = get_ota_disk_space_up__get_packed_size(&rsp_pkg);

	unsigned char *out = malloc(len);
	if(out != NULL)  {
		size_t pack_len = get_ota_disk_space_up__pack(&rsp_pkg, out);
		unsigned int frame_len = 0; 
		unsigned char *frame_buf = build_full_pb_tlv_frame(frame_head->seq, frame_head->cmd, RSP_CODE, out, pack_len, &frame_len);
		if(frame_buf) {
			ret = send_frame(param->udp_param.fd , get_real_frame_head(frame_buf), get_real_frame_len(frame_len), frame_head->cmd);
			free(frame_buf);
		}
		else {
			printf("build errrrrrrrrrrrrrrr\n");
		}
		free(out);

	}
	else {
		printf("malloc errrrrr\n");
	}

	return ret;
}


/*
 * 平台通知设备启动OTA升级 C6H
struct  StartOtaGetFirmwareDown
{
  ProtobufCMessage base;
  char *url;
  int32_t firmware_size;
  ProtobufCBinaryData firmware_crc16;
  char *firmware_ver;
  int32_t firmware_type;
  int32_t uint_type;
  protobuf_c_boolean radio;
  ProtobufCBinaryData node_id;
};
*/
int pb_cmd_ota_download_ready(const void *pdata, int inlen, const udp_socket_cb_t *param)
{    
   log_debug("%s:%s", log_tag, __FUNCTION__);
   const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
   const unsigned char *data = (unsigned char *)pdata;
   const frame_head_t *frame_head = &(tlv_frame->frame_head);
   data += sizeof(kt_tlv_frame_t);
   unsigned int len = tlv_frame->body_len;

	memcpy(&ota_socket_param, param, sizeof(ota_socket_param));
	int ack = 2;
	ota_param_t new_param={0};
	new_param.kt_download_cb = download_progress_callback;
	if(ota_busy_flag == 0) {
		StartOtaGetFirmwareDown *ppb_pkg = start_ota_get_firmware_down__unpack(NULL, len, data);
		if(ppb_pkg) {
			if(strlen(ppb_pkg->url)>=sizeof(new_param.url)) {
				log_debug("url:%s, should < %d!", ppb_pkg->url, sizeof(new_param.url));
				goto _err_exit;
				
			}
			strcpy(new_param.url, ppb_pkg->url);
			
			if(ppb_pkg->firmware_crc16.data==NULL || ppb_pkg->firmware_crc16.len !=2) {
				log_debug("rcv crc16 value err");
				goto _err_exit;
			}
			un_short_t un_crc;
			un_crc.oct[1] = ppb_pkg->firmware_crc16.data[0];
			un_crc.oct[0] = ppb_pkg->firmware_crc16.data[1];
			new_param.fw_crc = un_crc.num;
			
			if(ppb_pkg->firmware_ver == NULL) {
				log_debug("no get fw ver");
				goto _err_exit;
			}
			strncpy(new_param.fw_ver, ppb_pkg->firmware_ver, sizeof(new_param.fw_ver)-1);
			new_param.fw_size = ppb_pkg->firmware_size;
			new_param.fw_type = ppb_pkg->firmware_type;
			new_param.node_type = ppb_pkg->unit_type;
			if(ppb_pkg->file_name) {
				strncpy(new_param.real_name,  ppb_pkg->file_name, sizeof(new_param.real_name)-1);
			}
			
			if(ppb_pkg->radio) {
				new_param.un_mac.num64 = 0xffffffffffff;
			}
			else {
				if(ppb_pkg->node_id.len!=6 || ppb_pkg->node_id.data==NULL) {
					log_debug("target node id err");
					goto _err_exit;
				}
				mac_cp(new_param.un_mac.oct, ppb_pkg->node_id.data);
			}
		
			ack = 1;
			
			
_err_exit:
			start_ota_get_firmware_down__free_unpacked(ppb_pkg, NULL);
		}


	}
	else {
		ack = 3;
		log_notice("ota download busy now");
	}

	// rsp
	rsp_normal(frame_head->seq, frame_head->cmd, ack, param);


	if(ack == 1) {
		log_notice("start Ota now ......");
		memcpy(&pb_ota_param, &new_param, sizeof(ota_param_t));
		print_ota_param(&pb_ota_param);
		ota_task_init(&pb_ota_param);
		
		//ota_busy_flag = 1;
		//sleep(2);
	}
	
    return 0;
}
int unpack_ota_progress_rsp(const void *pdata, int len, const udp_socket_cb_t *param)
{
	const kt_tlv_frame_t *tlv_frame = (kt_tlv_frame_t*)pdata;
	const unsigned char *data = (unsigned char *)pdata;
	const frame_head_t *frame_head = &(tlv_frame->frame_head);
	data += sizeof(kt_tlv_frame_t);
	return 0;
}

#endif

/*
 * 平台通知设备启动OTA升级 C6H
struct  StartOtaGetFirmwareDown
{
  ProtobufCMessage base;
  char *url;
  int32_t firmware_size;
  ProtobufCBinaryData firmware_crc16;
  char *firmware_ver;
  int32_t firmware_type;
  int32_t uint_type;
  protobuf_c_boolean radio;
  ProtobufCBinaryData node_id;
};
*/







  //  StartOtaGetFirmwareDown ota_http_obj;

/**
 * @brief 
 * 
 * @param istream 
 * @param ostream 
 * @return uint16_t 
 */
 uint16_t pb_ota_http_start(pb_istream_t* istream, uint8_t* txbuf,uint8_t seq)
{
    int ack = tlv_ack_ok;
    uint16_t txlen ;
    StartOtaGetFirmwareDown ota_http_obj;
	ota_param_t new_param={0};

    tlv_t*   pf_tx = NULL;
    uint8_t* pb_tx = NULL;
    pb_ostream_t ostream;

    // tlv_t*   pf_tx = (tlv_t*)txbuf;
    // uint8_t* pb_tx = pf_tx->data + sizeof(tlv_head_t);
    // pb_ostream_t ostream = pb_ostream_from_buffer(pb_tx, TLV_TXPB_SIZE );


    LOG_NAME();

    memset(&ota_http_obj, 0, sizeof(StartOtaGetFirmwareDown)) ;
    memset(&new_param,0,sizeof(ota_param_t) );
	new_param.kt_download_cb = download_progress_callback; //注册回调函数


#if 0
	if( ota_busy_flag ) 
	{////????
		ack = tlv_ack_busy;
		log_notice("ota download busy now\n");
	    goto _err_exit;
	}
#endif



    if( !pb_decode(istream, StartOtaGetFirmwareDown_fields, &ota_http_obj) ) 
    {
		ack = tlv_ack_format_error;
		//log_notice("ota pb_decode err\n");
		logout("ota pb_decode err\n");
	    goto _err_exit;
	}


   ////url:http://110.41.4.96:8000/web/ota_firmware/downloadOTAFile/6b1384e3-fac7-411b-   
	logout("url:%s\n", ota_http_obj.url);

	if( strlen(ota_http_obj.url) >= sizeof(new_param.url) ) 
	{
		ack = tlv_ack_format_error;
		logout("url, too len, should be < %lu!",sizeof(new_param.url) );
		goto _err_exit;
		
	}

	// memset(new_param.url,0,sizeof(new_param.url) );
	strncpy(new_param.url, ota_http_obj.url, sizeof(new_param.url)-1 ) ;
	

   logout("OTA fw_type=%d,radio=%d,\n",ota_http_obj.firmware_type,ota_http_obj.radio );


     switch (ota_http_obj.firmware_type ) 
	 {
		case OTA_C1_6_FW_TYPE_HOST:
		{
            new_param.un_mac.num64 = 0xffffffffffff;
		}break;
	
		case OTA_C1_6_FW_TYPE_UNIT:
		{
			if( ota_http_obj.radio ) 
			{//true：表示对集中器下所有同unit_type的节点升级其Unit固件
			//广播升级
				new_param.un_mac.num64 = 0xffffffffffff;

			}else {// 单点升级

				if( ota_http_obj.node_id.size !=6 ) {

					ack = tlv_ack_invalid_id;
					logout("OTA node id size err\n");
					goto _err_exit;
				}

				memcpy(new_param.un_mac.oct, ota_http_obj.node_id.bytes, 6);
			}

		} break;
	
		case OTA_C1_6_FW_TYPE_PLC_MASTER:
		{
		     new_param.un_mac.num64 = 0xffffffffffff;

		} break;
		
		case OTA_C1_6_FW_TYPE_PLC_SLAVE:
		{
			if( ota_http_obj.radio ) 
			{//true：表示对集中器下所有同unit_type的节点升级其Unit固件
			//广播升级
				new_param.un_mac.num64 = 0xffffffffffff;

			}else {// 单点升级

				if( ota_http_obj.node_id.size !=6 ) 
				{
					ack = tlv_ack_invalid_id;
					logout("OTA node id size err\n");
					goto _err_exit;
				}

		       memcpy(new_param.un_mac.oct, ota_http_obj.node_id.bytes, 6);
			}

		} break;

		default:
		{
			ack = tlv_ack_invalid;  //当前不支持的功能
			logout("OTA node id size err\n");
			goto _err_exit;
		} break;
 
	 } ////----switch-----


    logout("OTA node mac=%0lx\n",new_param.un_mac.num64 );	

	ack = tlv_ack_ok;   

	strncpy(new_param.real_name,  ota_http_obj.file_name, sizeof(new_param.real_name)-1 );
	logout("fw_name=%s\n",ota_http_obj.file_name );	
	
	strncpy(new_param.fw_ver, ota_http_obj.firmware_ver, sizeof(new_param.fw_ver)-1);
	logout("fw_ver=%s\n",ota_http_obj.firmware_ver );

    //  //????crc32?? crc16??
	new_param.fw_crc    = ota_http_obj.firmware_crc16;

	new_param.fw_size   = ota_http_obj.firmware_size;	
	new_param.fw_type   = ota_http_obj.firmware_type;
	new_param.node_type = ota_http_obj.unit_type;

	logout("fw_size=%d,fw_crc=0x%04x,fw_type=%d,unit_type=%d,radio=%d\n",ota_http_obj.firmware_size,ota_http_obj.firmware_crc16 ,ota_http_obj.firmware_type,ota_http_obj.unit_type,ota_http_obj.radio );


_err_exit:

	//rsp_normal(frame_head->seq, frame_head->cmd, ack, param);

     pf_tx = (tlv_t*)txbuf;
     pb_tx = pf_tx->data + sizeof(tlv_head_t);
     ostream = pb_ostream_from_buffer(pb_tx, TLV_TXPB_SIZE );


	////-------先应答--------------
	txlen = pb_ack_encode(&ostream, ack); 
	txlen = tlv_seal(pf_tx, PB_CMD_OTA_HTTP_START, seq, txlen);
	plat_send( (uint8_t*)pf_tx, txlen);


	if( tlv_ack_ok == ack) 
	{
        ////接收无误，再启动升级进程--------------
		logout("start Ota now ......\n");
		memcpy(&pb_ota_param, &new_param, sizeof(ota_param_t));
		//print_ota_param(&pb_ota_param);
		ota_task_init(&pb_ota_param);
		
		//ota_busy_flag = 1;
		//sleep(2);
	}

 return 0;   ////上级不需要重复应答

 }




// 网关上报进度//设备主动上报OTA升级进度 C7H
static uint8_t pb_indicate_device_ota_progress(uint8_t err_code, uint8_t stage, uint8_t percent)
{
	uint8_t txbuf[256];
	uint16_t txlen;	

  LOG_NAME();

   txlen = pb_ota_http_process_report( txbuf, err_code,  stage, percent );  //打包数据---

return   plat_send(txbuf, txlen);  //发送TCP数据----

}





const char *get_ota_step_msg(unsigned int step)
{
	if(step >= sizeof(ota_step_str)/sizeof(ota_step_str[0])) {
		step = sizeof(ota_step_str)/sizeof(ota_step_str[0])-1;
	}
	return ota_step_str[step];
}

/*
functin: ota_progress_pro() 上报ota进度， 可用于网关http下载进度和网关传送给子设备进度
param: total, 总帧数数
       gotten, 已处理帧数
**/

int ota_error_report(int err_code, int stage, unsigned char per_in)
{
	#if 1
	int per;
	if(err_code) {
		per = per_in;
	}
	else {
		per = 100;
	}
	#endif
	
	//log_debug("ota err=%d(%s), stage=%d(%s)", err_code, get_err_msg(err_code), stage, get_ota_step_msg(stage));
	//return pb_indicate_device_ota_progress(err_code, stage, per, ota_socket_param.udp_param.fd);

	logout("%s() ota err=%d(%s), stage=%d(%s)", __FUNCTION__,err_code, get_err_msg(err_code), stage, get_ota_step_msg(stage));

	return pb_indicate_device_ota_progress(err_code, stage, per); 


}


 	// 	log_notice("%s......%s %d/%d (%g%%)\n", __FUNCTION__,progress_data, (unsigned int)dlnow, (unsigned int)dltotal, dlnow*100.0/dltotal);   //这里执行了

	// 	if(ota_param.kt_download_cb) {
	// 		log_notice("ota_param-callback\n"); 
	// 		ota_param.kt_download_cb((unsigned int)dltotal, (unsigned int)dlnow, OTA_STAGE_GW_DOWNLOAD);//这里也执行了		
    //        ////int download_progress_callback(unsigned int total, unsigned int gotten, int stage)
	// 	}
	// }



	// OTA_STAGE_GW_DOWNLOAD = 1, // 1： 集中器从平台下载文件
	// OTA_STAGE_GW_CHK = 2,      // 2： 集中器文件校验
	// OTA_STAGE_GW_SAVE,         // 3： 集中器烧写Flash


///////回调函数 download_progress_pro--------------发送给节点的协议-----
int download_progress_callback(unsigned int total, unsigned int gotten, int stage)
{
	unsigned int per;

  //  LOG_NAME(); 

	if( gotten != total )
	{
		per = (gotten*100)/total;
	}else {
		per = 100;
	}
	


#if 0	

	//int update_interval = 2;	
	static time_t last_second =0; //debug  ///=0;//debug
	time_t timep;
	time(&timep);

	if( (timep - last_second >= 1) || (per==100) )   ///	if(timep - last_second >= update_interval || per==100  )
	{
		last_second = timep;
       //设备主动上报OTA升级进度 C7H
		logout("download callback: stage=%d, done:%u/%u=%u%%\n", stage, gotten, total, per);
		pb_indicate_device_ota_progress(0, stage, per); ///这里没有执行

	}else {
		logout("download callback: not report now\n");   ////执行的是这里
	}
	
#endif


   ////ota_progress err_code=0,stage=1,percent=0,
	//设备主动上报OTA升级进度 C7H
	 logout("%s() stage=%d, done:%u/%u=%u%%\n",__FUNCTION__, stage, gotten, total, per);
	 pb_indicate_device_ota_progress(OTA_ERR_CODE_SUCCESSED, stage, per); 

	return 0;

}


// download callback: not report now
// down_progress_func......*****###  1737570/2071515 (83.8792%)


#if 0
///////回调函数 
int download_progress_pro(unsigned int total, unsigned int gotten, int stage)
{
	int update_interval = 2;

    LOG_NAME();  


	if(stage == OTA_STAGE_GW_DOWNLOAD) 
	{
		update_interval = 1;
		gotten = 1;
	}else //if(stage == OTA_STAGE_TRANSMIT_NODE) //debug 
	{
		update_interval = 2;
	}

	unsigned int per;
	if(gotten != total)
	{
		per = gotten*100/total;
	}else {
		per = 100;
	}
	
	static time_t last_second=0;//debug
	time_t timep;
	time(&timep);

	if( (timep - last_second >=update_interval) || (per==100) )
	{
		last_second = timep;

		if(stage == OTA_STAGE_GW2CCO) {
			per >>= 1;

		}else if(stage == OTA_STAGE_CCO2STA)
		 {
			per = (per>>1)+50;
			if(per > 100)
			{
				per = 100;
			}
		}

       //设备主动上报OTA升级进度 C7H
		logout("stage=%d, done:%u/%u=%u%%\n", stage, gotten, total, per);
		pb_indicate_device_ota_progress(0, stage, per); ///这里没有执行

	}else {
		logout("download_progress: not report now\n");   ////执行的是这里
	}
	

	// //设备主动上报OTA升级进度 C7H
	// logout("stage=%d, done:%u/%u=%u%%\n", stage, gotten, total, per);
	// pb_indicate_device_ota_progress(0, stage, per); ///这里没有执行


	return 0;

}


#endif





